#ifndef system_h_
#define system_h_

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
#include <stdio.h>
#include "GLee.h"

#define KEY_DOWN(k)   (GetAsyncKeyState(k) < 0)

namespace util
{
	static const float PI = 3.1415926535897932384626433832795f;
	struct vec2   { float x, y; };
	struct vec3   { float x, y, z; };
	vec2  v2gen(float x, float y);
	vec3  v3gen(float x, float y, float z);
	vec3  v3add(const vec3& a, const vec3& b);
	vec3  v3sub(const vec3& a, const vec3& b);
	vec3  v3mul(const vec3& v, float f);
	vec3  v3cross(const vec3& a, const vec3& b);
	float v3dot(const vec3& a, const vec3& b);
	float v3len(const vec3& v);
	vec3  v3nor(const vec3& v);
	vec3  v3rotY(const vec3& v, float ang);
	vec3  v3rotZ(const vec3& v, float ang);
	
	struct color   { float c[4]; };
	color c4gen(float r, float g, float b, float a);
	// Non Copy ///////////////////////////////////////////////////////////////////////////////
	class CNonCopy
	{
	public:
		CNonCopy()   {}
		~CNonCopy()  {}
	private:
		CNonCopy(const CNonCopy&);
		CNonCopy& operator=(const CNonCopy&);
	};
	// system ///////////////////////////////////////////////////////////////////////////////
	typedef void (*callback)(void);
	class CSystem : private CNonCopy
	{	
	public:
		CSystem(unsigned int w, unsigned int h, const char* t);
		~CSystem();
		
		enum Func { INIT, FRAME, RENDER };
		void set_callback(Func option, callback p_func);
        
		void run();
		unsigned long tick(void) const;
		int fps(void) const;
        
		LRESULT msg_proc(UINT msg, WPARAM wparam, LPARAM lparam);
	
	private:
	    void create_window(const char* title);
	    void syserr(void) const;
		void glerr(void) const;
	    CSystem();
		
	private:
		HWND m_hwnd;
		HDC m_hdc;
		HGLRC m_hrc;
		bool m_paused;
		callback m_p_init_func;
		callback m_p_frame_func;
		callback m_p_render_func;
		unsigned int m_fixed_delta;
		unsigned int m_win_width, m_win_height;
		int m_fps;
	};
	extern CSystem* g_p_system;
	// geometry ////////////////////////////////////////////////////////////////////////////////////////
	class CGeometry : private CNonCopy
	{
	public:
		CGeometry();
		~CGeometry();
	
		void create_cube(void);
		void create_sphere(int slices);
		void create_torus(int precision);
		void create_floor();
		const float* get_vertex(void) const;
		const float* get_normal(void) const;
		const float* get_texcoord(void) const;
		const short* get_index_data(void) const;
		int get_index_num(void) const;
	
		void clear(void);
	
	private:
		float* m_pv;   // vertex array
		float* m_pn;   // normal array
		float* m_pt;   // texcoord array
		short* m_pidx; // index array
		int m_nidx;    // index num
	};
	class CObj : private CNonCopy
	{
	public:
		CObj();
		~CObj();
	
		bool load(const char* file);
		void unload(void);
		
		const float* get_vertex(void) const;
		const float* get_normal(void) const;
		const float* get_texcoord(void) const;
		const int vertex_num(void) const;
		
		void build_normal(void);
	private:
		float* m_pv;   // vertex array
		float* m_pn;   // normal array
		float* m_pt;   // texcoord array
		int m_nvrts;   // number of vertexs , each vertex has 3 float
		
		void calculate_normal(float* v1, float* v2, float* v3, float* nor);
	};
	// texture ////////////////////////////////////////////////////////////////////////////////////////
	class CTexture : private CNonCopy
	{
	public:
		CTexture();
		~CTexture();
		
		void set_filter_low(void);
		void set_filter_middle(void);
		void set_filter_high(void);
		bool load_bmp(const char* file);
		GLuint get_id(void) const;
		
	private:
		GLuint m_id;
		int m_filter;
	};
	// camera ////////////////////////////////////////////////////////////////////////////////////////
	class CCamera : private CNonCopy
	{
	public:
		CCamera();
		~CCamera();
		
		void perspective(float fov, float aspect, float znear, float zfar);
		void lookat(float eyex, float eyey, float eyez,
					float centerx, float centery, float centerz,
					float upx, float upy, float upz);

		void reset(void);
		void build(void);
		
		void yaw(float ang);
		void roll(float ang);
		void pitch(float ang);
		
		void walk(float dt);
		void strafe(float dt);
		void fly(float dt);
		
	private:
		vec3 m_eye;
		vec3 m_look;
		vec3 m_up;
		vec3 m_right;
	};
}

#endif
